package com.tsugun.mediaplayer.presenter.manager

import android.app.Notification
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.media.session.PlaybackState
import android.os.RemoteException
import android.support.v4.app.NotificationCompat
import android.support.v4.app.NotificationManagerCompat
import android.support.v4.media.MediaMetadataCompat
import android.support.v4.media.session.MediaControllerCompat
import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat

import com.tsugun.mediaplayer.R
import com.tsugun.mediaplayer.presenter.service.MusicAudioService
import com.tsugun.mediaplayer.presenter.utils.LogHelper
import com.tsugun.mediaplayer.presenter.utils.ResourceHelper
import com.tsugun.mediaplayer.view.activity.IndexActivity

/**
 * 通知管理类
 * Created by shize on 2017/6/16.
 */

class MediaNotificationCompatManager(private val mService: MusicAudioService) : BroadcastReceiver() {
    private var mSessionToken: MediaSessionCompat.Token? = null
    private var mController: MediaControllerCompat? = null
    private var mTransportControls: MediaControllerCompat.TransportControls? = null

    private var mPlaybackState: PlaybackStateCompat? = null
    private var mMetadata: MediaMetadataCompat? = null

    private val mNotificationManager: NotificationManagerCompat

    private val mPauseIntent: PendingIntent
    private val mPlayIntent: PendingIntent
    private val mPreviousIntent: PendingIntent
    private val mNextIntent: PendingIntent

    private val mNotificationColor: Int

    private var mStarted = false

    init {
        updateSessionToken()

        mNotificationColor = ResourceHelper.getThemeColor(mService,
                android.R.attr.colorPrimary, Color.DKGRAY)

        mNotificationManager = NotificationManagerCompat.from(mService)

        val pkg = mService.packageName
        mPauseIntent = PendingIntent.getBroadcast(mService, REQUEST_CODE,
                Intent(ACTION_PAUSE).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT)
        mPlayIntent = PendingIntent.getBroadcast(mService, REQUEST_CODE,
                Intent(ACTION_PLAY).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT)
        mPreviousIntent = PendingIntent.getBroadcast(mService, REQUEST_CODE,
                Intent(ACTION_PREV).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT)
        mNextIntent = PendingIntent.getBroadcast(mService, REQUEST_CODE,
                Intent(ACTION_NEXT).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT)

        mNotificationManager.cancelAll()
    }

    /**
     * 开启通知
     */
    fun startNotification() {
        if (!mStarted) {
            mMetadata = mController!!.metadata
            mPlaybackState = mController!!.playbackState

            // The notification must be updated after setting started to true
            val notification = createNotification()
            if (notification != null) {
                mController!!.registerCallback(mCb)
                val filter = IntentFilter()
                filter.addAction(ACTION_NEXT)
                filter.addAction(ACTION_PAUSE)
                filter.addAction(ACTION_PLAY)
                filter.addAction(ACTION_PREV)
                mService.registerReceiver(this, filter)

                mService.startForeground(NOTIFICATION_ID, notification)
                mStarted = true
            }
        }
    }

    /**
     * 停止通知
     */
    fun stopNotification() {
        if (mStarted) {
            mStarted = false
            mController!!.unregisterCallback(mCb)
            try {
                mNotificationManager.cancel(NOTIFICATION_ID)
                mService.unregisterReceiver(this)
            } catch (ex: IllegalArgumentException) {
                // ignore if the receiver is not registered.
            }

            mService.stopForeground(true)
        }
    }

    override fun onReceive(context: Context, intent: Intent) {
        val action = intent.action
        when (action) {
            ACTION_PAUSE -> mTransportControls!!.pause()
            ACTION_PLAY -> mTransportControls!!.play()
            ACTION_NEXT -> mTransportControls!!.skipToNext()
            ACTION_PREV -> mTransportControls!!.skipToPrevious()
        }
    }

    /**
     * 更新会话
     */
    private fun updateSessionToken() {
        val freshToken = mService.sessionToken
        if (mSessionToken == null || mSessionToken != freshToken) {
            if (mController != null) {
                mController!!.unregisterCallback(mCb)
            }
            mSessionToken = freshToken
            try {
                mController = MediaControllerCompat(mService, mSessionToken!!)
            } catch (e: RemoteException) {
                e.printStackTrace()
            }

            mTransportControls = mController!!.transportControls
            if (mStarted) {
                mController!!.registerCallback(mCb)
            }
        }
    }

    private fun createContentIntent(): PendingIntent {
        val openUI = Intent(mService, IndexActivity::class.java)
        openUI.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
        return PendingIntent.getActivity(mService, REQUEST_CODE, openUI,
                PendingIntent.FLAG_CANCEL_CURRENT)
    }

    private val mCb = object : MediaControllerCompat.Callback() {
        override fun onPlaybackStateChanged(state: PlaybackStateCompat?) {
            mPlaybackState = state
            if (state != null && (state.state == PlaybackState.STATE_STOPPED || state.state == PlaybackState.STATE_NONE)) {
                stopNotification()
            } else {
                val notification = createNotification()
                if (notification != null) {
                    mNotificationManager.notify(NOTIFICATION_ID, notification)
                }
            }
        }

        override fun onMetadataChanged(metadata: MediaMetadataCompat?) {
            mMetadata = metadata
            val notification = createNotification()
            if (notification != null) {
                mNotificationManager.notify(NOTIFICATION_ID, notification)
            }
        }

        override fun onSessionDestroyed() {
            super.onSessionDestroyed()
            updateSessionToken()
        }
    }

    private fun createNotification(): Notification? {
        if (mMetadata == null || mPlaybackState == null) {
            return null
        }

        val notificationBuilder = NotificationCompat.Builder(mService)
        val playPauseButtonPosition = 1
        notificationBuilder.addAction(R.drawable.ic_skip_previous_black,
                mService.getString(R.string.str_skip_previous), mPreviousIntent)

        addPlayPauseAction(notificationBuilder)

        notificationBuilder.addAction(R.drawable.ic_skip_next_black,
                mService.getString(R.string.str_skip_next), mNextIntent)

        val description = mMetadata!!.description

        var fetchArtUrl: String? = null
        var art: Bitmap? = null
        if (description.iconUri != null) {
            val artUrl = description.iconUri!!.toString()
            art = BitmapFactory.decodeFile(artUrl)
            if (art == null) {
                fetchArtUrl = artUrl
                art = BitmapFactory.decodeResource(mService.resources,
                        R.drawable.ic_app_status)
            }
        }

        notificationBuilder
                .setStyle(android.support.v7.app.NotificationCompat.MediaStyle()
                        .setShowActionsInCompactView(
                                *intArrayOf(playPauseButtonPosition))
                        .setMediaSession(mSessionToken))
                .setColor(mNotificationColor)
                .setSmallIcon(R.drawable.ic_app_status)
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
                .setUsesChronometer(true)
                .setContentIntent(createContentIntent())
                .setContentTitle(description.title)
                .setContentText(description.subtitle)
                .setLargeIcon(art)

        setNotificationPlaybackState(notificationBuilder)
        if (fetchArtUrl != null) {
            notificationBuilder.setLargeIcon(BitmapFactory.decodeFile(fetchArtUrl))
            mNotificationManager.notify(NOTIFICATION_ID, notificationBuilder.build())
        }

        return notificationBuilder.build()
    }

    private fun addPlayPauseAction(builder: NotificationCompat.Builder) {
        val label: String
        val icon: Int
        val intent: PendingIntent
        if (mPlaybackState!!.state == PlaybackState.STATE_PLAYING) {
            label = mService.getString(R.string.str_pause)
            icon = R.drawable.ic_pause_black
            intent = mPauseIntent
        } else {
            label = mService.getString(R.string.str_play)
            icon = R.drawable.ic_play_arrow_black
            intent = mPlayIntent
        }
        builder.addAction(NotificationCompat.Action(icon, label, intent))
    }

    private fun setNotificationPlaybackState(builder: NotificationCompat.Builder) {
        if (mPlaybackState == null || !mStarted) {
            mService.stopForeground(true)
            return
        }
        if (mPlaybackState!!.state == PlaybackState.STATE_PLAYING && mPlaybackState!!.position >= 0) {
            builder
                    .setWhen(System.currentTimeMillis() - mPlaybackState!!.position)
                    .setShowWhen(true)
                    .setUsesChronometer(true)
        } else {
            builder
                    .setWhen(0)
                    .setShowWhen(false)
                    .setUsesChronometer(false)
        }

        builder.setOngoing(mPlaybackState!!.state == PlaybackState.STATE_PLAYING)
    }

    companion object {
        private val TAG = LogHelper.TAG

        private val NOTIFICATION_ID = 412
        private val REQUEST_CODE = 100

        val ACTION_PAUSE = "com.tusgun.mediaplayer.mediabrowserservice.pause"
        val ACTION_PLAY = "com.tusgun.mediaplayer.mediabrowserservice.play"
        val ACTION_PREV = "com.tusgun.mediaplayer.mediabrowserservice.prev"
        val ACTION_NEXT = "com.tusgun.mediaplayer.mediabrowserservice.next"
    }
}
